import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MultiThreadBounceGame extends JFrame {
    private static final int WIDTH = 800;
    private static final int HEIGHT = 600;
    private static final int PADDLE_WIDTH = 100;
    private static final int PADDLE_HEIGHT = 15;
    private static final int BALL_SIZE = 20;

    private final GamePanel gamePanel;
    private final List<Ball> balls = new ArrayList<>();
    private volatile Paddle paddle;
    private ExecutorService executor;
    private volatile int score = 0;
    private volatile boolean gameRunning = true;

    public MultiThreadBounceGame() {
        setTitle("Multi-thread Bounce Game");
        setSize(WIDTH, HEIGHT);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLocationRelativeTo(null);

        gamePanel = new GamePanel();
        add(gamePanel);

        initializeGame();
        setupControls();

        executor = Executors.newCachedThreadPool();
        startGame();
    }

    private void initializeGame() {
        synchronized (this) {
            paddle = new Paddle(WIDTH / 2 - PADDLE_WIDTH / 2, HEIGHT - 50,
                    PADDLE_WIDTH, PADDLE_HEIGHT);

            balls.clear();
            for (int i = 0; i < 3; i++) {
                balls.add(createRandomBall());
            }
        }
    }

    private Ball createRandomBall() {
        Random rand = new Random();
        return new Ball(rand.nextInt(WIDTH - BALL_SIZE),
                rand.nextInt(HEIGHT / 2),
                rand.nextInt(5) + 2,
                rand.nextInt(5) + 2);
    }

    private void setupControls() {
        addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                synchronized (paddle) {
                    if (e.getKeyCode() == KeyEvent.VK_LEFT) {
                        paddle.moveLeft();
                    } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
                        paddle.moveRight();
                    }
                }
                SwingUtilities.invokeLater(gamePanel::repaint);
            }
        });
    }

    private void startGame() {
        gameRunning = true;

        // Start ball threads
        synchronized (balls) {
            for (Ball ball : balls) {
                ball.setAlive(true);
                executor.execute(() -> {
                    while (ball.isAlive() && !Thread.currentThread().isInterrupted()) {
                        try {
                            ball.move();
                            checkCollisions(ball);
                            updateGameState();
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                    }
                });
            }
        }

        // Start render thread
        new Thread(() -> {
            while (gameRunning) {
                try {
                    SwingUtilities.invokeAndWait(() -> gamePanel.repaint());
                    Thread.sleep(16); // ~60 FPS
                } catch (Exception e) {
                    Thread.currentThread().interrupt();
                }
            }
        }).start();
    }

    private void checkCollisions(Ball ball) {
        // Wall collisions
        if (ball.x <= 0 || ball.x >= WIDTH - BALL_SIZE) {
            ball.reverseX();
        }
        if (ball.y <= 0) {
            ball.reverseY();
        }

        // Paddle collision
        synchronized (paddle) {
            if (ball.getBounds().intersects(paddle.getBounds())) {
                ball.reverseY();
                score += 10;
            }
        }

        // Ball-ball collisions
        synchronized (balls) {
            balls.stream()
                    .filter(other -> other != ball)
                    .filter(other -> ball.getBounds().intersects(other.getBounds()))
                    .forEach(other -> {
                        ball.reverseX();
                        ball.reverseY();
                        other.reverseX();
                        other.reverseY();
                    });
        }

        // Game over check
        if (ball.y >= HEIGHT - BALL_SIZE) {
            ball.setAlive(false);
            synchronized (balls) {
                if (balls.stream().noneMatch(Ball::isAlive)) {
                    endGame();
                }
            }
        }
    }

    private void updateGameState() {
        if (Math.random() < 0.002) {
            Ball newBall = createRandomBall();
            synchronized (balls) {
                balls.add(newBall);
            }
            executor.execute(() -> {
                while (newBall.isAlive() && !Thread.currentThread().isInterrupted()) {
                    try {
                        newBall.move();
                        checkCollisions(newBall);
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
            });
        }
    }

    private void endGame() {
        gameRunning = false;
        SwingUtilities.invokeLater(() -> {
            Object[] options = {"重新开始", "退出"};
            int choice = JOptionPane.showOptionDialog(this,
                    "游戏结束！得分: " + score,
                    "Game Over",
                    JOptionPane.DEFAULT_OPTION,
                    JOptionPane.INFORMATION_MESSAGE,
                    null,
                    options,
                    options[0]);

            if (choice == 0) {
                restartGame();
            } else {
                System.exit(0);
            }
        });
    }

    private void restartGame() {
        executor.shutdownNow();
        try {
            Thread.sleep(100); // Allow time for thread termination
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        initializeGame();
        score = 0;
        executor = Executors.newCachedThreadPool();
        startGame();
    }

    class GamePanel extends JPanel {
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            // Draw background
            g.setColor(Color.BLACK);
            g.fillRect(0, 0, WIDTH, HEIGHT);

            // Draw paddle
            synchronized (paddle) {
                g.setColor(Color.GREEN);
                g.fillRect(paddle.x, paddle.y, paddle.width, paddle.height);
            }

            // Draw balls
            synchronized (balls) {
                g.setColor(Color.RED);
                for (Ball ball : balls) {
                    if (ball.isAlive()) {
                        g.fillOval((int) ball.x, (int) ball.y, BALL_SIZE, BALL_SIZE);
                    }
                }
            }

            // Draw score
            g.setColor(Color.WHITE);
            g.setFont(new Font("Arial", Font.BOLD, 20));
            g.drawString("Score: " + score, 10, 30);
        }
    }

    static class Ball {
        private double x, y;
        private double dx, dy;
        private volatile boolean alive = true;

        public Ball(double x, double y, double dx, double dy) {
            this.x = x;
            this.y = y;
            this.dx = dx;
            this.dy = dy;
        }

        public void move() {
            x += dx;
            y += dy;
        }

        public Rectangle getBounds() {
            return new Rectangle((int) x, (int) y, BALL_SIZE, BALL_SIZE);
        }

        public void reverseX() {
            dx = -dx;
        }

        public void reverseY() {
            dy = -dy;
        }

        public boolean isAlive() {
            return alive;
        }

        public void setAlive(boolean alive) {
            this.alive = alive;
        }
    }

    static class Paddle {
        private int x, y;
        private final int width, height;
        private static final int SPEED = 15;

        public Paddle(int x, int y, int width, int height) {
            this.x = x;
            this.y = y;
            this.width = width;
            this.height = height;
        }

        public void moveLeft() {
            x = Math.max(0, x - SPEED);
        }

        public void moveRight() {
            x = Math.min(WIDTH - width, x + SPEED);
        }

        public Rectangle getBounds() {
            return new Rectangle(x, y, width, height);
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            MultiThreadBounceGame game = new MultiThreadBounceGame();
            game.setVisible(true);
        });
    }
}